#ifndef ELAPSED_TIMER_H
#define ELAPSED_TIMER_H

#include <chrono>

/**
 * \class ElapsedTimer
 * \brief A tiny elapsed timer, header only.
 *
 * Example usage:
 * \code
 *  ElapsedTimer<int64_t> elapsedTimer;
 *  for (int i = 0; i < 10000; ++i) {
 *      // do something
 *  }
 *  std::cout << "Elapsed milliseconds: " << elapsedTimer.elapsed();
 *  elapsedTimer.restart();
 * \endcode
 */
template<class TimeT = double>
class ElapsedTimer
{
public:
    using chrono_clock_type = std::chrono::high_resolution_clock;
    using time_point = typename chrono_clock_type::time_point;
    using time_type = TimeT;

    /**
     * Constructs a elapsed timer, and start timing.
     */
    ElapsedTimer() : tp(chrono_clock_type::now()) { }

    /**
     * Restarts the timer.
     */
    inline void restart()
    {
        tp = chrono_clock_type::now();
    }

    /**
     * Returns the number of milliseconds since this ElapsedTimer was last started.
     */
    inline time_type elapsed() const
    {
        return elapsed_milliseconds();
    }

    /**
     * Returns the number of nanoseconds since this ElapsedTimer was last started.
     */
    inline time_type elapsed_nanoseconds() const
    {
        return std::chrono::duration_cast<nanoseconds_t>(chrono_clock_type::now() - tp).count();
    }

    /**
     * Returns the number of microseconds since this ElapsedTimer was last started.
     */
    inline time_type elapsed_microseconds() const
    {
        return std::chrono::duration_cast<microseconds_t>(chrono_clock_type::now() - tp).count();
    }

    /**
     * Returns the number of milliseconds since this ElapsedTimer was last started.
     */
    inline time_type elapsed_milliseconds() const
    {
        return std::chrono::duration_cast<milliseconds_t>(chrono_clock_type::now() - tp).count();
    }

    /**
     * Returns the number of seconds since this ElapsedTimer was last started.
     */
    inline time_type elapsed_seconds() const
    {
        return std::chrono::duration_cast<seconds_t>(chrono_clock_type::now() - tp).count();
    }

    /**
     * Returns the number of minutes since this ElapsedTimer was last started.
     */
    inline time_type elapsed_minutes() const
    {
        return std::chrono::duration_cast<minutes_t>(chrono_clock_type::now() - tp).count();
    }

    /**
     * Returns the number of hours since this ElapsedTimer was last started.
     */
    inline time_type elapsed_hours() const
    {
        return std::chrono::duration_cast<hours_t>(chrono_clock_type::now() - tp).count();
    }

private:
    using nanoseconds_t = std::chrono::duration<time_type, std::chrono::nanoseconds::period>;
    using microseconds_t = std::chrono::duration<time_type, std::chrono::microseconds::period>;
    using milliseconds_t = std::chrono::duration<time_type, std::chrono::milliseconds::period>;
    using seconds_t = std::chrono::duration<time_type, std::chrono::seconds::period>;
    using minutes_t = std::chrono::duration<time_type, std::chrono::minutes::period>;
    using hours_t = std::chrono::duration<time_type, std::chrono::hours::period>;

    time_point tp;
};

#endif // ELAPSED_TIMER_H